--[[
模块名称：prot32960
模块功能：32960协议实现
模块最后修改时间：2019.04.18
]]

local lpack = require"pack"

module(...,package.seeall)

local slen,sbyte,ssub,sgsub,schar,srep,smatch,sgmatch = string.len,string.byte,string.sub,string.gsub,string.char,string.rep,string.match,string.gmatch
local sformat = string.format
local bpack = lpack.pack
local bunpack = lpack.unpack
-- enum cloudcmd_t
-- {
	CMD_VEH_SIGNIN = 0x01
	CMD_RT_UPLOAD = 0x02
	CMD_FIX_UPLOAD = 0x03
	CMD_VEH_SIGNOUT = 0x04

	CMD_PLT_SIGNIN = 0x05
	CMD_PLT_SIGNOUT = 0x06

	HEARTBEAT = 0x07
	CLOCK_CORRECT = 0x08
	CMD_UPLOAD_RESV = 0x09
	CONFIG_QUERY = 0x80
	CONFIG_SETUP = 0x81
	CONTROL = 0x82

	CMD_DOWNLOAD_RESV = 0x83
	-- //RESV to 0xBF
	CMD_PLT_RESV = 0xC0
	-- //RESV to 0xFE
	CMD_END = 0xFF
-- };

    VEHICLE=0x01 --// 整车数据
    MOTOR=0x02 --// 驱动电机数据
    FUEL_CELL=0x03  --// 燃料电池数据
    ENGINE=0x04 --// 发动机数据
    LOCATION=0x05  --// 车辆位置
    EXTREMUM=0x06 --// 极值数据
    ALARM=0x07  --// 报警数据
    BATTERY_VOLTAGE=0x08 --// 可充电储能装置电压数据
	BATTERY_TEMPERATURE=0x09 --// 可充电储能装置温度数据
	CUSTOM_CONTROL_STATE=0x81 --控车状态

	SUCCESS=0x01
    FAILED=0x02 --// 错误；设置未成功
    VIN_DUP=0x03--// VIN重复；VIN重复错误
    COMMAND=0xFE--// 命令；表示数据包为命令包，而非应答包

local PROTOVERSION = 0
local serial = 0
local imei -- 命令头包含IMEI,在第一次获取以后做缓存
local get
local sendFunc,sucFunc

local function print(...)
	_G.print("protoair32960",...)
end

function bcd(d,n)
	local l = slen(d or "")
	local num
	local t = {}

	for i=1,l,2 do
		num = tonumber(ssub(d,i,i+1),16)

		if i == l then
			num = 0xf0+num
		else
			num = (num%0x10)*0x10 + num/0x10
		end

		table.insert(t,num)
	end

	local s = schar(_G.unpack(t))

	l = slen(s)

	if l < n then
		s = s .. srep("\255",n-l)
	elseif l > n then
		s = ssub(s,1,n)
	end

	return s
end

local function unbcd(d)
	local byte,v1,v2
	local t = {}

	for i=1,slen(d) do
		byte = sbyte(d,i)
		v1,v2 = bit.band(byte,0x0f),bit.band(bit.rshift(byte,4),0x0f)

		if v1 == 0x0f then break end
		table.insert(t,v1)

		if v2 == 0x0f then break end
		table.insert(t,v2)
	end

	return table.concat(t)
end

-----------------------------------------------------------------------------------------------------------------------





local function computeCrc(indata)
    local r = 0
    for i = 1, slen(indata) do
        local charcode = sbyte(indata, i, i)
		r = bit.bxor(r, charcode)
    end
    return r
end

function packAll(t,d)
	local base = lpack.pack(">bb",0x23,0x23) --head
	local extend
	extend = lpack.pack(">bb",t,SUCCESS) --cmd and resp

	if not imei then imei = get("IMEI") end
	s = imei
	l = slen(s)
	if l < 17 then s = srep("0",17-l) .. s end
	extend = extend..s --vin
	extend = extend..lpack.pack(">b",0x01) --enc
	extend = extend..lpack.pack(">H",slen(d)) --len
	extend = extend..d --data
	local r = computeCrc(extend)
	return base..extend..lpack.pack(">b",r)
end

function indexNum()
	serial = serial + 1
	if serial > 65531 then serial = 0 end
	return serial
end

function pack(t,...)
	local function empty(d)
		return true
	end

	local function setTime()
		tmp = misc.getClock()
        local utcTime = common.timeZoneConvert(tmp.year,tmp.month,tmp.day,tmp.hour,tmp.min,tmp.sec,8,0)
		year = utcTime["year"]
        year = ssub(year,3,-1)
		time = lpack.pack(">b6",year, utcTime["month"], utcTime["day"], utcTime["hour"], utcTime["min"], utcTime["sec"])
		log.error("--zbb--protoair32960.setTime:",string.toHex(time))
		return time
    end

	local function login(dat)
		local se = indexNum()

		iccid = get("ICCID")
		l = slen(iccid)
		if l < 20 then iccid = srep("0",20-l) .. iccid end

		local bcount = 3
		local blen = 5
		local tbl = {}
		table.insert(tbl, lpack.pack(">bb",bcount,blen))
		for i = 1, bcount do
			table.insert(tbl, "12345")
		end
		return setTime()..lpack.pack(">H",se)..iccid..table.concat(tbl)
	end

	local function logout()
		local se = indexNum()
		return setTime()..lpack.pack(">H",se)
	end

	local function heart()
		return ""
	end

	GEAR_0 = 0x00 --空档
	GEAR_1 = 0x01 --1档
	GEAR_2 = 0x02 --2档
	GEAR_3 = 0x03 --3档
	GEAR_4 = 0x04 --4档
	GEAR_5 = 0x05 --5档
	GEAR_6 = 0x06 --6档
	GEAR_7 = 0x07 --7档
	GEAR_8 = 0x08 --8档
	GEAR_9 = 0x09 --9档
	GEAR_10 = 0x0A --10档
	GEAR_R = 0x0D --倒档
	GEAR_D = 0x0E --D档
	GEAR_P = 0x0F --P档

	local function pack_VEHICLE()
		head = lpack.pack(">b",VEHICLE)

		speed = 63.5*10
		totalMilege = 18123*10
		voltage = 132.4 * 10 --总电压
		current = (1200.4 + 1000) * 10 --总电流
		stateOfCharge = 90 --% SOC
		dcInverterStateValue = 0x01 --DC-DC状态
		gearPosition = GEAR_0 --档位 1b
			+ 0x10 * 1 --4bit 有无制动力
			+ 0x20 * 0 --5bit 有无驱动力
		insulance = 1000 --绝缘电阻
		acceleratorTravel = 10 --加速踏板行程值 0-100
		brakeTravel = 0 --制动踏板状态 0-100
		 --车辆状态 充电状态 运行模式 车速 累计里程 总电压 总电流 SOC DC-DC状态 档位 绝缘电阻 预留
		b = lpack.pack(">b3HIHHb3Hbb",0x01,0x01,0x01,
			speed,totalMilege,voltage,
			current,stateOfCharge,dcInverterStateValue,
			gearPosition,insulance,acceleratorTravel,brakeTravel)
		return head..b
	end

	local function pack_MOTOR()
		head = lpack.pack(">b",MOTOR)
		
		count = lpack.pack(">b",2) --电机个数

		local tbl = {}
		table.insert(tbl, count)
		for i=1,2 do
			motorSeq = 240
			status = 0x01
			controllerTemperature = 100
			motorSpeed = 10000 + 20000
			motorTorque = (10 +2000) * 10
			motorTemperature = 10  + 40
			controllerVoltage = 400.2 * 10
			controllerCurrent = (98 + 1000) * 10
			b = lpack.pack(">b3HHbHH",motorSeq,status,controllerTemperature,motorSpeed,motorTorque,motorTemperature,controllerVoltage,controllerCurrent)
			table.insert(tbl, b)
		end
		return head..table.concat(tbl)
	end

	local function pack_FUEL_CELL()
		head = lpack.pack(">b",FUEL_CELL)

		fuelCellVoltage = 1 * 10
		fuelCellCurrent = 1241*10
		fuelConsumptionRate = 12.4 * 100

		number = 10
		b = lpack.pack(">H3H",fuelCellVoltage,fuelCellCurrent,fuelConsumptionRate,number)
		
		local tbl = {}
		table.insert(tbl, b)
		for i=1,number do
			probeTemperatureValue = 120 + 40
			bb = lpack.pack(">b",probeTemperatureValue)
			table.insert(tbl, bb)
		end
		highestTempOfHydrogenSystem = (120 + 40)*10
		highestTempProbeCodeOfHydSys = 10
		highestConOfHydrogen = 11
		highestHyConSensorCode = 1
		hydrogenMaxPressure = 1 * 10
		hydrogenMaxPressureSensorCode = 1
		highVoltageDcStateValue = 1
		c = lpack.pack(">HbHbHbb",highestTempOfHydrogenSystem,
			highestTempProbeCodeOfHydSys,highestConOfHydrogen,
			highestHyConSensorCode,hydrogenMaxPressure,
			hydrogenMaxPressureSensorCode,highVoltageDcStateValue)
		table.insert(tbl, c)
		return head..table.concat(tbl)
	end

	local function pack_LOCATION(gpsStat)
		head = lpack.pack(">b",LOCATION)

		locState = (gpsStat.fix and 0 or 1) * 0x01 
			 + (gpsStat.lngType == "S" and 1 or 0) * 0x02 
			 + (gpsStat.latType == "W" and 1 or 0) * 0x04
		longitude = tonumber(gpsStat.lng) * 1000000
		latitude = tonumber(gpsStat.lat) * 1000000
		b = lpack.pack(">bII",locState,longitude,latitude)
		return head .. b
	end

	local function pack_EXTREMUM()
		head = lpack.pack(">b",EXTREMUM)

		voltageMaxSubsystem_ = 12
		voltageMaxBattery_ = 22
		maxVoltage_ = 111 * 1000
		voltageMinSubsystem_ = 2
		voltageMinBattery_ = 3
		minVoltage_ = 19 * 1000
		temperatureMaxSubsystem_ = 34
		temperatureMaxProbe_ = 45
		maxTemperature_ = 145
		temperatureMinSubsystem_ = 15
		temperatureMinProbe_ = 15
		minTemperature_ = 16
		b = lpack.pack(">b2Hb2Hb6",voltageMaxSubsystem_,voltageMaxBattery_,maxVoltage_,
					   voltageMinSubsystem_,voltageMinBattery_,minVoltage_,
					   temperatureMaxSubsystem_,temperatureMaxProbe_,maxTemperature_,
					   temperatureMinSubsystem_,temperatureMinProbe_,minTemperature_)
		return head .. b
	end

	local function pack_ALARM()
		head = lpack.pack(">b",ALARM)

		maxAlarmLevel = 1
		alarmBitIdentify = 1
		b = lpack.pack(">bI",maxAlarmLevel,alarmBitIdentify)
		
		local tbl = {}
		table.insert(tbl, b)

		batteryFaultCount = 2
		b1 = lpack.pack(">b",batteryFaultCount)
		table.insert(tbl, b1)
		for i=1,batteryFaultCount do
			batteryFaultData = 120
			local c = lpack.pack(">I",batteryFaultData)
			table.insert(tbl, c)
		end

		motorFaultCount = 2
		b2 = lpack.pack(">b",motorFaultCount)
		table.insert(tbl, b2)
		for i=1,motorFaultCount do
			motorFaultData = 120
			local c = lpack.pack(">I",motorFaultData)
			table.insert(tbl, c)
		end

		engineFaultCount = 2
		b3 = lpack.pack(">b",engineFaultCount)
		table.insert(tbl, b3)
		for i=1,engineFaultCount do
			engineFaultData = 120
			local c = lpack.pack(">I",engineFaultData)
			table.insert(tbl, c)
		end

		otherFaultCount = 2
		b4 = lpack.pack(">b",otherFaultCount)
		table.insert(tbl, b4)
		for i=1,otherFaultCount do
			otherFaultData = 120
			local c = lpack.pack(">I",otherFaultData)
			table.insert(tbl, c)
		end
		return head .. table.concat(tbl)
	end

	local function pack_BAT_VOL()
		head = lpack.pack(">b",BATTERY_VOLTAGE)
		cnt = 16
		count = lpack.pack(">b",cnt) --个数

		local tbl = {}
		table.insert(tbl, count)
		for i=1,cnt do
			chargeableSubSystemNumber_ = i
			voltage_ = 4.2 * 10
			current_ = (10 + 1000) * 10
			batteryTotalCount_ = 11000
			frameStartBatterySeq_ = 101
			b = lpack.pack(">bHHHH",chargeableSubSystemNumber_,voltage_,current_,
			    batteryTotalCount_,frameStartBatterySeq_)
			table.insert(tbl, b)

			ccc = 10
			count1 = lpack.pack(">b",ccc) --个数
			table.insert(tbl, count1)
			for i=1,ccc do
				batteryVoltage_ = 3.543 * 1000
				b1 = lpack.pack(">H",batteryVoltage_)
				table.insert(tbl, b1)
			end
		end
		return head..table.concat(tbl)
	end

	local function pack_BAT_TEMP()
		head = lpack.pack(">b",BATTERY_TEMPERATURE)
		cnt = 16
		count = lpack.pack(">b",cnt) --个数

		local tbl = {}
		table.insert(tbl, count)
		for i=1,cnt do
			subSystemNumber_ = i
			b = lpack.pack(">b",subSystemNumber_)
			table.insert(tbl, b)

			ccc = 10
			count1 = lpack.pack(">H",ccc) --个数
			table.insert(tbl, count1)
			for i=1,ccc do
				probeTemperature_ = 46 + 40
				b1 = lpack.pack(">b",probeTemperature_)
				table.insert(tbl, b1)
			end
		end
		return head..table.concat(tbl)
	end

	local function pack_CUST_CTL_ST()
		head = lpack.pack(">b",CUSTOM_CONTROL_STATE)

		local tbl = {}
		count = lpack.pack(">H",1)
		table.insert(tbl, count)

		b1 = lpack.pack(">b",20)
		table.insert(tbl, b1)
		return head..table.concat(tbl)
	end

	local function rtReport(gpsStat,devStat)
		local tbl = {}
		table.insert(tbl, setTime())
		table.insert(tbl, pack_VEHICLE())
		table.insert(tbl, pack_MOTOR())
		-- table.insert(tbl, pack_FUEL_CELL()) --ignore
		-- table.insert(tbl, pack_ENGINE()) --ignore
		table.insert(tbl, pack_LOCATION(gpsStat))
		table.insert(tbl, pack_EXTREMUM())
		table.insert(tbl, pack_ALARM())
		table.insert(tbl, pack_BAT_VOL())--// 可充电储能装置 电压数据
		table.insert(tbl, pack_BAT_TEMP())--// 可充电储能装置 温度数据
		table.insert(tbl, pack_CUST_CTL_ST())
		return table.concat(tbl)
	end

	local function control(cmdV)
		local tbl = {}
		table.insert(tbl, setTime())
		table.insert(tbl, lpack.pack(">b",cmdV))
		return table.concat(tbl)
	end

	local function configQ(ids)
		local tbl = {}
		table.insert(tbl, setTime())
		table.insert(tbl, lpack.pack(">b",#ids))
		for i=1, #ids do
			id = ids[i]
			log.error("--zbb--protoair32960.unpack configQ id=",id)
			if id==1 then
				table.insert(tbl, lpack.pack(">b",1)) --STORAGE_PERIOD
				table.insert(tbl, lpack.pack(">H",20)) --STORAGE_PERIOD
			end
			if id==2 then
				table.insert(tbl, lpack.pack(">b",2)) --NORMAL_REPORT_INTERVAL
				table.insert(tbl, lpack.pack(">H",20))
			end
			if id==3 then
				table.insert(tbl, lpack.pack(">b",3)) --ALARM_REPORT_INTERVAL
				table.insert(tbl, lpack.pack(">H",20))
			end
		end 
		return table.concat(tbl)
	end

	local function configS()
		local tbl = {}
		table.insert(tbl, setTime())
		return table.concat(tbl)
	end

    local procer = {
		[CMD_VEH_SIGNIN] = login,
		[CMD_RT_UPLOAD] = rtReport,
		[CMD_FIX_UPLOAD] = rtReport,
		[CMD_VEH_SIGNOUT] = logout,
		[CMD_UPLOAD_RESV] = empty,
		[CONTROL] = control,
		[CONFIG_QUERY] = configQ,
		[CONFIG_SETUP] = configS,
		[HEARTBEAT] = heart,
	}	

	local s = procer[t](...)
	print("pack end",t,string.toHex(s))
	return s
end

function setInitFunc(sfunc,sucfunc)
	sendFunc = sfunc
	sucFunc = sucfunc
end

function init()
	d = pack(CMD_VEH_SIGNIN,1)
	sendFunc(CMD_VEH_SIGNIN,d)
    return 0
end

function logout()
	d = pack(CMD_VEH_SIGNOUT)
	sendFunc(CMD_VEH_SIGNOUT,d)
    return 0
end

function heart()
	d = pack(HEARTBEAT)
	sendFunc(HEARTBEAT,d)
end

function loc(t,stat,adcr)
	log.error("--zbb--protoair32960.loc:",t)
	d = pack(CMD_RT_UPLOAD,t,stat)
	sendFunc(CMD_RT_UPLOAD,d)
end

function locbatch(msgs)
	local count = #msgs
	for i=1,count do
		local msg = msgs[i]
		log.info("--zbb--protoair32960.locbatch:",i,string.toHex(msg))
		sendFunc(CMD_FIX_UPLOAD,msg)
	end
end
---------------------------------------------------------------------------------------

--[[
函数名：unpack
功能  ：根据ptotobuffer协议进行封包
参数  ：
		s:需要解析字符串
返回值：解包字符串
]]
function unpack(s)
	local packet = {}
		
	local function empty(dat, res)
		return true
	end
			
	local function loginRes(dat, res)
		if slen(dat) > 0 then
			extpoz,y,m,d,hh,mm,ss,seq=bunpack(dat,">b6H")
			log.error("--zbb--protoair32960.unpack loginRes=",y,m,d,hh,mm,ss,seq)
			sucFunc()
			-- return proc[packet.cmd](p) and packet or nil
		end
	end
			
	local function rtReportRes(dat, res)
		if slen(dat) > 0 then
			extpoz,res2=bunpack(dat,">b")
			log.error("--zbb--protoair32960.unpack rtReportRes=",res,res2)
		end
	end
			
	local function logoutRes(dat, res)
		if slen(dat) > 0 then		
			extpoz,y,m,d,hh,mm,ss,seq=bunpack(dat,">b6H")
			-- log.error("--zbb--protoair32960.unpack error data5=",y,m,d,hh,mm,ss,seq)
			-- sucFunc()
		end
	end
			
	local function control(dat, res)
		if res ~= COMMAND then
			log.error("--zbb--protoair32960.unpack control res ~= COMMAND",res)
		end
		if slen(dat) > 0 then		
			extpoz,y,m,d,hh,mm,ss,cmdVal=bunpack(dat,">b6b")
			log.error("--zbb--protoair32960.unpack control=",y,m,d,hh,mm,ss,cmdVal)
			--2关机，3复位，4恢复出厂，5断开数据
			if cmdVal == 2 then
			elseif cmdVal == 3 then
			elseif cmdVal == 4 then
			elseif cmdVal == 5 then
			elseif cmdVal == 6 then
				local level = string.byte(dat,extpoz)
				local content = ssub(dat,extpoz+1,-2)
	            log.error("--zbb--protoair32960.unpack warning=",level,content)
			elseif cmdVal == 1 then--upgrade
				local content = ssub(dat,extpoz,-2)
	            log.error("--zbb--protoair32960.unpack upgrade=",content)
			end
			d = pack(CONTROL,cmdVal)
	        sendFunc(CONTROL,d)
		end
	end
	-- STORAGE_PERIOD(1),
    -- NORMAL_REPORT_INTERVAL(2),
    -- ALARM_REPORT_INTERVAL(3),
    -- MANAGEMENT_PLATFORM_DOMAIN_LENGTH(4),
    -- MANAGEMENT_PLATFORM_DOMAIN(5),
    -- MANAGEMENT_PLATFORM_PORT(6),
    -- HARDWARE_VERSION(7),
    -- FIRMWARE_VERSION(8),
    -- HEARBEAT_INTERVAL(9),
    -- TERMINAL_RESPONSE_TIMEOUT(10),
    -- PLATFORM_RESPONSE_TIMEOUT(11),
    -- LOGIN_RETRY_INTERVAL(12),
    -- PUBLIC_PLATFORM_DOMAIN_LENGTH(13),
    -- PUBLIC_PLATFORM_DOMAIN(14),
    -- PUBLIC_PLATFORM_PORT(15),
    -- SAMPLING(16),
	local function configQuery(dat, res)
		if res ~= COMMAND then
			log.error("--zbb--protoair32960.unpack configQuery res ~= COMMAND",res)
		end
		if slen(dat) > 0 then
			extpoz,y,m,d,hh,mm,ss,paCnt=bunpack(dat,">b6b")
			log.error("--zbb--protoair32960.unpack configQuery=",y,m,d,hh,mm,ss,paCnt)
			ids = {}
			for i=1,paCnt do
				id = string.byte(dat,extpoz+i-1)
				table.insert(ids, id)
				-- log.error("--zbb--protoair32960.unpack id=",id)
			end
			d = pack(CONFIG_QUERY,ids)
	        sendFunc(CONFIG_QUERY,d)
		end
	end
			
	local function configSetup(dat, res)
		if res ~= COMMAND then
			log.error("--zbb--protoair32960.unpack configSetup res ~= COMMAND",res)
		end
		if slen(dat) > 0 then
			extpoz,y,m,d,hh,mm,ss,paCnt=bunpack(dat,">b6b")
			log.error("--zbb--protoair32960.unpack configSetup=",y,m,d,hh,mm,ss,paCnt)
			skip = extpoz
			for i=1,paCnt do
				local f = string.byte(d,skip)
				skip = skip + 1
				if f == 1 then
					a,b = string.byte(d,skip,2)
					skip = skip + 2
					log.error("--zbb--protoair32960.unpack i=",skip,a,b)
				elseif f == 2 then
					a,b = string.byte(d,skip,2)
					skip = skip + 2
					log.error("--zbb--protoair32960.unpack i=",skip,a,b)
				elseif f == 3 then
					a,b = string.byte(d,skip,2)
					skip = skip + 2
					log.error("--zbb--protoair32960.unpack i=",skip,a,b)
				elseif f == 4 then
					a = string.byte(d,skip)
					skip = skip + 1
					log.error("--zbb--protoair32960.unpack i=",skip,a)
				elseif f == 5 then
					a,b = string.byte(d,skip,2)
					skip = skip + 2
					log.error("--zbb--protoair32960.unpack i=",skip,a,b)
				end
			end
			d = pack(CONFIG_SETUP,1)
	        sendFunc(CONFIG_SETUP,d)
		end
	end
	
	local procer = {
		[CMD_VEH_SIGNIN] = loginRes,
		[CMD_VEH_SIGNOUT] = logoutRes,
		[CMD_RT_UPLOAD] = rtReportRes,
		[CMD_FIX_UPLOAD] = rtReportRes,

		[CONTROL] = control,
		[CONFIG_QUERY] = configQuery,
		[CONFIG_SETUP] = configSetup,
		[HEARTBEAT] = empty,
	}
	
	local data2 = ssub(s, 3, -1)
	local r = computeCrc(data2)
	if r ~= 0 then
        log.error("--zbb--protoair32960.unpack error crc=",r)
        return false
	end

	extpoz,msgType,msgRes,imei,encType,msgLen=bunpack(data2,">bbA17bH") --ResponseTag
	log.error("--zbb--protoair32960.unpack imei=",msgType,msgRes,imei,encType,msgLen)
	local data4 = ssub(data2,extpoz,-1)
	log.error("--zbb--protoair32960.unpack data4=",string.toHex(data4))

	packet.id = msgType
	procer[msgType](data4, msgRes)
	return packet or nil
end

function reget(id)
	get = id
end
